package com.apes.scm.sal.saleOrder;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.event.Listener;
import com.apes.framework.util.*;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.Stock;
import com.apes.scm.account.account.repository.InventoryBatchRepository;
import com.apes.scm.account.account.repository.StockRepository;
import com.apes.scm.account.account.service.ProductAccountService;
import com.apes.scm.account.account.service.StockService;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.card.model.InsuranceCard;
import com.apes.scm.card.repository.InsuranceCardRepository;
import com.apes.scm.invoice.model.Invoice;
import com.apes.scm.invoice.repository.InvoiceRepository;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Brand;
import com.apes.scm.masterdata.product.model.Category;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.model.ProductUom;
import com.apes.scm.masterdata.product.repository.ProductRepository;
import com.apes.scm.masterdata.product.repository.ProductUomRepository;
import com.apes.scm.masterdata.product.service.UomService;
import com.apes.scm.masterdata.stock.model.Location;
import com.apes.scm.masterdata.stock.repository.LocationRepository;
import com.apes.scm.sal.saleOrder.model.Coupons;
import com.apes.scm.sal.saleOrder.model.SaleOrderCoupons;
import com.apes.scm.sal.saleOrder.repository.CouponsRepository;
import com.apes.scm.sal.saleOrder.repository.SaleOrderCouponsRepository;
import com.apes.scm.stock.transfer.model.Pick;
import com.apes.scm.stock.transfer.model.PickItem;
import com.apes.scm.stock.transfer.repository.PickRepository;
import com.apes.scm.voucher.service.VoucherType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能：销售订单服务
 *
 * @author xul
 * @create 2018-04-03 16:56
 */
@Service("saleService")
public class SaleOrderService extends DomainService {

    @Autowired
    private SaleOrderRepository saleOrderRepository;

    @Autowired
    private LedgerService ledgerService;

    @Autowired
    private ProductAccountService productAccountService;

    @Autowired
    private SaleOrderItemRepository saleOrderItemRepository;

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ProductUomRepository productUomRepository;

    @Autowired
    private StockRepository stockRepository;

    @Autowired
    private UomService uomService;

    @Autowired
    private InvoiceRepository invoiceRepository;

    @Autowired
    private LocationRepository locationRepository;

    @Autowired
    private DeptRepository deptRepository;

    @Autowired
    private CouponsRepository couponsRepository;

    @Autowired
    private SaleOrderCouponsRepository saleOrderCouponsRepository;

    @Autowired
    private InsuranceCardRepository insuranceCardRepository;

    public static final List<String> PLATFORM_OUT_ORDER = Arrays.asList("富登","师人订制");

    /**
     * 功能：创建订单
     */
    public SaleOrder create(SimpleRequest request) {
        SaleOrder saleOrder = request.getO(SaleOrder.class);
        saleOrder.setState("sent");
        saleOrder.setInvoiceStatus("no");

        // 检查库存
        if (!saleOrder.getLocation().getCompany().getId().equals(saleOrder.getCompany().getId())) {
            throw new RuntimeException("库存位置对应的公司与做单公司不一致，请检查数据！");
        }

        saleOrder.computeAmt(); // 合计金额
        checkSaleVaild(saleOrder);  // 验证数据合法性
        saleOrder.judgeUnitPrice();
        SaleOrder saleOrderNew = saleOrderRepository.saveAndFlush(saleOrder);
        //占用可用数
        saleOrderNew.getSaleItems().stream()
                .filter( saleOrderItem -> saleOrderItem.getProductRule() == 0 )
                .forEach((saleOrderItem -> productAccountService(saleOrderNew, saleOrderItem, -1.00, "分销订单新增")));
        charge(saleOrderNew, "occupy_unsettled", saleOrderNew.getAmtTotal() - saleOrderNew.getDiscount()); // 增加占款未清项
        transaction(saleOrderNew, "discount_unsettled", -1 * saleOrderNew.getDiscount(), "discount_freeze", saleOrderNew.getDiscount()); //减少返利未清项增加返利冻结项
        // 若佣金对账单标记勾选，则绑定卡券
        saleOrderCoupons(saleOrderNew);
        return saleOrderNew;
    }

    /**
     * 功能: 修改
     * @param request
     * @return
     */
    public SaleOrder update(SimpleRequest request) {
        SaleOrder saleOrder = request.getO(SaleOrder.class);
        SaleOrder oldSaleOrder = saleOrderRepository.findOne(saleOrder.getId());
        saleOrder.computeAmt(); // 合计金额

        // 反向释放
        charge(saleOrder,"occupy_unsettled", -1 * oldSaleOrder.getAmtTotal() + oldSaleOrder.getDiscount());
        transaction(saleOrder, "discount_freeze", -1 * oldSaleOrder.getDiscount(), "discount_unsettled", oldSaleOrder.getDiscount());
        checkSaleVaild(saleOrder);  // 验证数据合法性

        // 返利金释放冻结
        transaction(saleOrder, "discount_freeze", saleOrder.getDiscount(), "discount_unsettled", -1 * saleOrder.getDiscount());
        saleOrder.judgeUnitPrice();
        // 占款释放写入
        charge(saleOrder,"occupy_unsettled", saleOrder.getAmtTotal() - saleOrder.getDiscount());
        oldSaleOrder.getSaleItems().forEach((saleOrderItem -> productAccountService(oldSaleOrder, saleOrderItem, 1.00, "分销订单修改")));
        SaleOrder saleOrderNew = saleOrderRepository.saveAndFlush(saleOrder);
        // 可用数先增加后减少
        saleOrderNew.getSaleItems().forEach((saleOrderItem -> productAccountService(saleOrderNew, saleOrderItem, -1.00,"分销订单修改")));
        // 若佣金对账单标记勾选，则绑定卡券
        saleOrderCoupons(saleOrderNew);
        return saleOrderNew;
    }

    /**
     * 功能：删除订单
     */
    public String delete(SimpleRequest request) {

        String id = request.get("id");
        SaleOrder saleOrder = saleOrderRepository.findOne(id);
        if (saleOrder == null) throw new RuntimeException("订单：" + request.get("id") + " 在系统中不存在。");
        if (saleOrder.getState().equals("confirmed")) throw new RuntimeException("订单：" + request.get("id") + " 在系统中已审批。");

        // 加可用数
        saleOrder.getSaleItems().stream().forEach((saleOrderItem -> productAccountService(saleOrder, saleOrderItem, 1.00, "分销订单删除")));

        // 返利金减少冻结项,增加未清项
        transaction(saleOrder, "discount_freeze", -1 * saleOrder.getDiscount(), "discount_unsettled", saleOrder.getDiscount());

        //占款未清项减少
        charge(saleOrder, "occupy_unsettled", -1 * saleOrder.getAmtTotal() + saleOrder.getDiscount());

        saleOrderRepository.delete(saleOrder);
        // 若已勾选佣金对账单则删除并解锁对应卡券
        unlockCouponsByDelete(saleOrder);
        return "删除成功！";
    }

    /**
     * 功能：审批订单
     *
     * @param request
     * @return
     */
    public SaleOrder approve(SimpleRequest request) {
        SaleOrder oldSaleOrder = request.getO(SaleOrder.class);
        int version = oldSaleOrder.getVersion();
        String id = oldSaleOrder.getId();
        SaleOrder saleOrder = saleOrderRepository.findOne(id);
        if (saleOrder == null) throw new RuntimeException("订单：" + id + " 在系统中不存在。");
        if (saleOrder.getVersion() != version) throw new RuntimeException("记录已更新或已删除，请重新提交！");
        if (saleOrder.getState().equals("confirmed")) throw new RuntimeException("订单：" + id + " 在系统中已审批。");
        saleOrder.setState("confirmed");
        saleOrder.setDateApprove(new Date());

        SaleOrder nSaleOrder = saleOrderRepository.saveAndFlush(saleOrder);
        //如果无需发货直接设置完成状态
        nSaleOrder.setShipped();

        if (nSaleOrder.getState().equals("done")) nSaleOrder = saleOrderRepository.saveAndFlush(nSaleOrder);

        return nSaleOrder;
    }



    /**
     * 写入总账
     */
    private void transaction(SaleOrder saleOrder, Object... items) {
        ledgerService.transaction(saleOrder.getCompany(), saleOrder.getDept(), saleOrder.getCustomer(),
                saleOrder.getCooperation().getInventoryMode(), saleOrder.getCooperation().getAccountingMethod(),
                saleOrder.getId(), saleOrder.getInnerBusinessType(), new Date(), items);
    }

    /**
     * 写入总账
     */
    private void charge(SaleOrder saleOrder, String ledgerAccount, Double incurred) {
        ledgerService.charge(saleOrder.getCompany(), saleOrder.getDept(), saleOrder.getCustomer(),
                saleOrder.getCooperation().getInventoryMode(), saleOrder.getCooperation().getAccountingMethod(),
                saleOrder.getId(), saleOrder.getClass().getSimpleName(), new Date(), ledgerAccount, incurred);
    }

    /**
     * 可用数
     */
    private void productAccountService(SaleOrder saleOrder, SaleOrderItem saleOrderItem, Double unit, String remarks) {
        if (LogicalUtil.in(saleOrderItem.getProduct().getType(),"aut")) {
            return;
        }
        productAccountService.setProductAvailable(saleOrderItem.getProduct(),
                saleOrder.getLocation(), saleOrderItem.getUom(), unit * saleOrderItem.getProductUomQty(), saleOrder.getInnerBusinessType(), saleOrder.getId(), saleOrderItem.getId(), remarks,saleOrder.getCompany(),"overSale");
    }


    /**
     * 验证销售的合法性(返利金 + 合计 <= 可用余额)
     */
    private void checkSaleVaild(SaleOrder saleOrder) {
        PartyRole customer = saleOrder.getCustomer();
        PartyRole company = saleOrder.getCompany();
        Map<String, Double> ledgerSummary = getCurAmountMap(saleOrder.getDept().getCompany(), customer);
        //内协销售 不判断客户账期
        if (!Objects.equals("INNER_SALE",saleOrder.getOrderType())){
            //新增检查客户账期
            checkPartnerAccountPeriod(company, customer,ledgerSummary.get("period"));
        }
        // 验证可用额度足够
        if (!customer.getParty().isRole("Company")) { /**内部客户不做额度检查**/
            double balance = ledgerSummary.get("credit") + ledgerSummary.get("prepayment_unsettled") - ledgerSummary.get("current_unsettled");
            if (Arith.round((saleOrder.getAmtTotal() - saleOrder.getDiscount() - balance), 2) > 0.001) {
                try {
                    throw new RuntimeException("[" + company.getId() + "]公司与合作伙伴[" + customer.getId() + "]信用额度不足！请联系财务对接人增加信用额度！");
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                } finally {
                    EventUtil.sendSynEvent(this, "event:dingding.message.publish", MapUtil.mapper(
                            "userList", "055352295820066798,02753,195734655926089443,01232364531421522622,255204694439702617,050055382220899756",
                            "content", "销售金额大于可用余额，请重新确认, 当前可用余额为:"
                                    + Arith.round(balance, 2) + "，公司：" + company.getId() + ",合作伙伴：" + customer.getId()
                    ));
                }
            }
        }
        saleOrder.getSaleItems().stream().forEach(saleOrderItem -> {
            //验证商品是否可用
            if (PLATFORM_OUT_ORDER.contains(saleOrderItem.getProduct().getBrand().getName()))
                throw new RuntimeException(saleOrderItem.getProduct().getName()+" 商品属于定制款商品,请前往黑盒商城下单! !");
        });

        // 验证返利金未清项足够
        if(saleOrder.getDiscount() >  ledgerSummary.get("discount_unsettled")){
            throw new RuntimeException("返利金额大于可用返利金，请重新确认, 当前可用返利余额为:" + ledgerSummary.get("discount_unsettled").toString()
                    + "，公司："+ company.getId() + "，部门："+"，合作伙伴：" + customer.getId());
        }
    }


    /***
     * 获取可用返利与可用额度
     * @return
     */
    public Map<String, Double> getCurAmount(SimpleRequest request) {
        JSONObject tempJson = request.getJO();
        PartyRole partner = new PartyRole();
        Dept dept = SpringManager.getBean(DeptRepository.class).findOne(tempJson.getJSONObject("dept").getString("id"));
        partner.setId(tempJson.getJSONObject("customer").getString("id"));
        return getCurAmount(dept.getCompany(), partner);
    }

    public Map<String, Double> getCurAmount(PartyRole company, PartyRole partner) {
        Map<String, Double> returnMap = new HashMap<>();
        Map<String, Double> ledgerSummary = getCurAmountMap(company, partner);
        returnMap.put("curBalance", ledgerSummary.get("credit") + ledgerSummary.get("prepayment_unsettled") - ledgerSummary.get("current_unsettled"));
        returnMap.put("curDiscount", ledgerSummary.get("discount_unsettled") != null ? ledgerSummary.get("discount_unsettled") : 0.00);
        return returnMap;
    }

    private Map<String, Double> getCurAmountMap(PartyRole company, PartyRole partner) {
        Map<String, Double> ledgerSummary = ledgerService.summary(
                MapUtil.mapper("company",company,  "partner", partner));
        ledgerSummary.putIfAbsent("credit", 0.00);
        ledgerSummary.putIfAbsent("prepayment_unsettled", 0.00);
        ledgerSummary.putIfAbsent("occupy_unsettled", 0.00);
        ledgerSummary.putIfAbsent("discount_unsettled", 0.00);
        ledgerSummary.putIfAbsent("current_unsettled", 0.00);
        ledgerSummary.putIfAbsent("period", 0.00);
        return ledgerSummary;
    }

    /**
     * 检查客户账期
     */
    public void checkPartnerAccountPeriod(PartyRole company,  PartyRole partner, Double period){
        if (period == 0) throw new RuntimeException(String.format("客户未设置账期,请联系财务对接人设置账期,客户[%s]公司[%s]",partner.getId(),company.getId()));
        Invoice invoice = invoiceRepository.findFirstByCompanyIdAndPartnerId(company.getId(), partner.getId());
        if (invoice != null){
            Date now = DateUtil.dateToDate(new Date(),DateUtil.SHOW_DATE_FORMAT);
            Date createDate = DateUtil.dateToDate(invoice.getCreateDate(),DateUtil.SHOW_DATE_FORMAT);
            long betweenDays = (now.getTime() - createDate.getTime()) / (1000L*3600L*24L) - 1;
            if (betweenDays > period) throw new RuntimeException(String.format("客户[%s],公司[%s]存在超过账期的待结算数据，请联系财务对接人核实",partner.getId(),company.getId()));
        }
    }

    // 获取实际可用数
    public Double getUsableQtyToUom(SimpleRequest request) {
        JSONObject tempJson = request.getJO();
        String id = tempJson.getString("id");
        Location location = new Location();
        PartyRole company = new PartyRole();
        Product product = productRepository.findOne(tempJson.getJSONObject("product").getString("id"));
        ProductUom uom = productUomRepository.findOne(tempJson.getJSONObject("uom").getLong("id"));
        location.setId(tempJson.getJSONObject("location").getString("id"));
        company.setId(tempJson.getJSONObject("company").getString("id"));
        return getUsableQtyToUom(location, product, uom, company, id);
    }

    /***
     * 功能：获取实际可用数
     * @param location
     * @param product
     * @param uom
     * @param company
     * @return
     */
    public Double getUsableQtyToUom(Location location, Product product, ProductUom uom, PartyRole company, String id){
        // 基础计量单位
        SaleOrderItem saleOrderItem = saleOrderItemRepository.findOne(id);
        ProductUom baseUom = uomService.getUom(company, product);

        // 剩余可用数
        Double restUsableQty = 0.00;
        List<Stock> procuctList = stockRepository.findAll(JpaDsl.toCriteria(MapUtil.mapper("location", location, "product", product)));
        if( procuctList != null && procuctList.size() > 0) restUsableQty = procuctList.get(0).getMarketableQty();

        // 已占用可用数
        Double occupyUsableQty = saleOrderItem.getUom().computeQty(saleOrderItem.getProductUomQty(), baseUom,false);

        return baseUom.computeQty(occupyUsableQty + restUsableQty, uom, true);
    }

    // 创建交货单
    public Pick createPickOrder(SimpleRequest request){
        SaleOrder saleOrder = request.getO(SaleOrder.class);
        Pick pick = new Pick();
        pick.setMoveType(saleOrder.getMoveType());        // 交货方式 direct：部分交货，one： 一次性交货
        pick.setCompany(saleOrder.getCompany());    // 设置公司
        pick.setLocation(saleOrder.getLocation());                // 设置仓库
        pick.setPartner(saleOrder.getCustomer());              // 合作伙伴 收货方的公司

        pick.setOrigin(saleOrder.getId());
        pick.setOriginBusinessType(saleOrder.getInnerBusinessType());
        pick.setOperation("deliver");                   // 交货类型：交货

        pick.setScene(saleOrder.getScene());                // 注入场景
        pick.setPickItems(createPickOrderItem(saleOrder));          // 添加明细

        return pick;
    }

    private Set<PickItem> createPickOrderItem(SaleOrder saleOrder){
        Set<PickItem> pickItems = saleOrder.getSaleItems().stream().filter(item -> LogicalUtil.notIn(item.getProduct().getType(), "aut")).map(
                (saleOrderItem) -> {
                    PickItem pickItem = new PickItem();
                    pickItem.setProduct(saleOrderItem.getProduct());
                    pickItem.setUom(saleOrderItem.getUom());
                    pickItem.setOrderedQty(saleOrderItem.getProductUomQty());
                    pickItem.setProductUomQty(saleOrderItem.getProductUomQty());
                    pickItem.setOriginLine(saleOrderItem.getId());
                    return pickItem;
                }).collect(Collectors.toSet());
        return pickItems;
    }

    /**
     * 判断销售订单完成有两个来源 1. 取消 2. 发货
     * 功能：处理交货完成事件以及交货单取消事件
     */
    @Listener(topic = "event:stock.pick.delivery")
    public void delivery(SimpleRequest request) {
        mainDealLogic(request, true);
    }

    @Listener(topic = "event:stock.pick.cancel")
    public void cancel(SimpleRequest request) {
        mainDealLogic(request, false);
    }

    private void mainDealLogic(SimpleRequest request, Boolean isDelivery) {
        JSONObject param = request.getJO();
        if (!param.getString("originBusinessType").equals(SaleOrder.class.getSimpleName())) return;
        SaleOrder saleOrder = saleOrderRepository.findOne(param.getString("origin"));
        for (Map item : ((List<Map>) param.get("items"))) {
            saleOrder.getSaleItems().stream()
                    .filter( saleOrderItem -> saleOrderItem.getId().equals((String) item.get("originItemId")))
                    .forEach( saleOrderItem -> {
                        double qtyDone = ((Number) item.get("qtyDone")).doubleValue();
                        if(isDelivery) {
                            saleOrderItem.delivery(qtyDone);  // 写入交货货数量
                        }
                    });
        }
        // 更新交货状态以及单据状态
        saleOrder.setShipped();

        // 用于判断是否触发场景流
        if(saleOrder.getState().equals("confirmed")) saleOrderRepository.save(saleOrder);
        if(saleOrder.getState().equals("done")) saleOrderRepository.saveAndFlush(saleOrder);
    }

    /**
     * 将生成凭证的数据中的批次与供应商回写
     * @param request list
     */
    @Listener(topic = "event:saleOrder.setBatchAndSupplier")
    public void setBatchAndSupplier(SimpleRequest request){
        JSONObject mainJson = request.getJO();
        if (mainJson.getJSONArray("items").size() == 0) return;

        SaleOrder saleOrder = saleOrderRepository.findOne(mainJson.getString("id"));
        if (Objects.isNull(saleOrder)) throw new RuntimeException("更新分销单明细行凭证数量时没有找到分销单："+ mainJson.get("id") );

        saleOrder.getSaleItems().forEach( saleOrderItem -> {
            mainJson.getJSONArray("items").forEach( x -> {
                JSONObject json = (JSONObject) x;
                String itemId = json.getString("id");
                String supplierId = json.getString("supplierId");
                long batchId = json.getLong("batchId");

                if (!itemId.equals(saleOrderItem.getId())) return;

                // 油漆凭证单独处理
                if (VoucherType.PAINT_PRODUCT.equals(json.getString("classification"))) {
                    saleOrderItem.setPaintFlag(true);
                    saleOrderItem.setPaintPrice(json.getDoubleValue("priceUint"));
                }

                InventoryBatch batch =  SpringManager.getBean(InventoryBatchRepository.class).findById(batchId).orElseThrow(() -> new RuntimeException("未找到批次信息，编码为：" + batchId));
                PartyRole supplier = SpringManager.getBean(PartyRoleRepository.class).findById(supplierId).orElseThrow(() -> new RuntimeException("未找到供应商，编码为："+ supplierId));
                saleOrderItem.setInventoryBatch(batch);
                saleOrderItem.setSupplier(supplier);
            });
        });
        saleOrderRepository.saveAndFlush(saleOrder);
    }

    public JSONObject gePriceAndStockOfGoods(SimpleRequest request) {
        JSONObject param = request.getJO();

        // 商品信息
        JSONObject condition = param.getJSONObject("condition");
        String productId = condition.getString("id");
        Product product = getProduct(productId);

        // 界面信息
        JSONObject conditionModel = param.getJSONObject("conditionModel");
        String locationId = conditionModel.getJSONObject("location").getString("id");
        Location location = getLocation(locationId);

        JSONObject scene = conditionModel.getJSONObject("scene");
        Map newMap = requestDJGC(product,location,scene);

        JSONObject priceData = new JSONObject();
        try {
            priceData = getDJGC(newMap);
        } catch (Exception e) {
            if (e.getMessage().contains("无符合条件")) throw new RuntimeException("联系采购制定采购价格文件,商品编码:" + productId);
            throw new RuntimeException(e.getMessage());
        }

        JSONObject result = new JSONObject();
        if (priceData.getDoubleValue("P004") <= 0)  throw new RuntimeException("请联系采购对接人定义商品" + productId + "采购价格文件");

        result.put("priceLimit", priceData.getDoubleValue("P005"));
        result.put("suggestPrice", priceData.getDoubleValue("P004"));
        result.put("priceUnit", priceData.getDoubleValue("P004"));
        result.put("priceTotal", priceData.getDoubleValue("P004") );
        result.put("product", product);
        result.put("uom", product.getUom());
        result.put("productUomQty", 1);
        result.put("marketableQty", condition.getDoubleValue("stockAmount"));

        return result;
    }

    /**
     * 封装策略入参
     * @param product
     * @param location
     * @param scene
     * @return
     */
    public Map requestDJGC(Product product,Location location,JSONObject scene){
        String platformId = SpringManager.getBean(StockService.class).getPlatform(false, true, location.getCompany(), product).getId();
        return MapUtil.mapper(
                "PRODUCT_ID", product.getId(),
                "CLASSIFY_ID", product.getClassify().getId(),
                "BRAND_ID", product.getBrand().getId(),
                "PRODUCT_UOM_ID", product.getUom().getId(),
                "SYS_RQ", new Date(),
                "PUR_COMPANY_ID", platformId,
                "SYS_NO_CHECK", "SUPPLIER_ID",
                "conditionStrategyId", "SAL002".equals(scene.getString("id")) ? "CG01" : "XS53"
        );
    }

    private Location getLocation(String locationId){
        Location location = locationRepository.findOne(locationId);
        if (Objects.isNull(location)) {
            throw new RuntimeException("未找到仓库信息，编码：" + locationId);
        }
        return location;
    }

    private Product getProduct(String productId){
        Product product = productRepository.findOne(productId);
        if (Objects.isNull(product)) {
            throw new RuntimeException("未找到商品信息,编码:" + productId);
        }
        return product;
    }

    public JSONObject getDJGC(Map map) {
        JSONObject response = new JSONObject();
        Object obj = this.invoke("condition.execConditionStrategy", map);

        response.put("obj", obj);
        JSONObject priceData = JSONObject.parseObject(response.getString("obj"));
        return priceData;
    }

    /**
     * 更新凭证数量，查看是否全部写完凭证数量
     *
     * @param request map
     */
    @Listener(topic = "event:voucher.save.qtyVouched")
    public void setQtyVouched(SimpleRequest request) {
        JSONObject mainJson = request.getJO();
        if (!VoucherType.CUSTOMER_SALE.equals(mainJson.getString("voucherType"))) return;

        // 来源为分销单写的凭证
        if (SaleOrder.class.getSimpleName().equals(mainJson.getString("businessType"))) {
            SaleOrder saleOrder = saleOrderRepository.findOne(mainJson.getString("id"));
            //if (Objects.isNull(saleOrder)) throw new RuntimeException("更新分销单明细行凭证数量时没有找到分销单数据："+ mainJson.get("id") );
            if (saleOrder != null) {
                setStateBySaleOrder(mainJson, saleOrder);
            }
        }

        // 分销模式下，交货单生成的凭证(多次交货)
        if (Pick.class.getSimpleName().equals(mainJson.getString("businessType"))) {
            Pick pick = SpringManager.getBean(PickRepository.class).findOne(mainJson.getString("id"));
            if (Objects.isNull(pick)) throw new RuntimeException("更新分销单明细行凭证数量时根据传入的交货单号没有找到数据："+ mainJson.get("id") );

            // 交货单的源为分销单
            if (SaleOrder.class.getSimpleName().equals(pick.getOriginBusinessType())) {
                SaleOrder saleOrder = saleOrderRepository.findOne(pick.getOrigin());
                if (Objects.isNull(saleOrder)) throw new RuntimeException("更新分销单明细行凭证数量时，根据交货单来源没有找到分销单数据："+ mainJson.get("id") );

                setStateByPickOrder(mainJson, saleOrder);
            }
        }

    }

    private void setStateBySaleOrder(JSONObject mainJson, SaleOrder saleOrder) {
        saleOrder.getSaleItems().forEach( saleItem -> {
            mainJson.getJSONArray("items").forEach(x -> {
                JSONObject item = (JSONObject) x;
                if (!saleItem.getId().equals(item.getString("id"))) return;
                saleItem.setQtyVoucher(item.getDouble("qty"));

                if (item.getLong("batchId") == null) return;
                long batchId = item.getLong("batchId");
                String supplierId = item.getString("supplierId");

                if (saleItem.getSupplier() == null) {
                    PartyRole supplier = SpringManager.getBean(PartyRoleRepository.class).findById(supplierId).orElseThrow(() -> new RuntimeException("未找到供应商，编码为：" + supplierId));
                    saleItem.setSupplier(supplier);
                }

                InventoryBatch batch = SpringManager.getBean(InventoryBatchRepository.class).findById(batchId).orElseThrow(() -> new RuntimeException("未找到批次信息，编码为：" + batchId));
                saleItem.setInventoryBatch(batch);
            });
        });

        long countNum = saleOrder.getSaleItems().stream()
                .filter(saleItem -> saleItem.getQtyVoucher() != saleItem.getProductUomQty())
                .count();

        if (countNum == 0) {
            saleOrder.setState("vouched");
            if (saleOrder.getDateDone() == null) saleOrder.setDateDone(new Date());     // 避免出现没有完成时间的情况
            saleOrderRepository.saveAndFlush(saleOrder);
        }
    }

    private void setStateByPickOrder(JSONObject mainJson, SaleOrder saleOrder) {
        saleOrder.getSaleItems().forEach( saleItem -> {
            mainJson.getJSONArray("items").forEach(x -> {
                JSONObject item = (JSONObject) x;

                if (!saleItem.getId().equals(item.getString("originItemId"))) return;
                saleItem.setQtyVoucher(item.getDouble("qty"));

                if (item.getLong("batchId") == null) return;
                long batchId = item.getLong("batchId");
                String supplierId = item.getString("supplierId");

                if (saleItem.getSupplier() == null) {
                    PartyRole supplier = SpringManager.getBean(PartyRoleRepository.class).findById(supplierId).orElseThrow(() -> new RuntimeException("未找到供应商，编码为：" + supplierId));
                    saleItem.setSupplier(supplier);
                }

                InventoryBatch batch = SpringManager.getBean(InventoryBatchRepository.class).findById(batchId).orElseThrow(() -> new RuntimeException("未找到批次信息，编码为：" + batchId));
                saleItem.setInventoryBatch(batch);
            });
        });

        long countNum = saleOrder.getSaleItems().stream()
                .filter(saleItem -> saleItem.getQtyVoucher() != saleItem.getProductUomQty())
                .count();

        if (countNum == 0) {
            saleOrder.setState("vouched");
            if (saleOrder.getDateDone() == null) saleOrder.setDateDone(new Date());      // 避免出现没有完成时间的情况
            saleOrderRepository.saveAndFlush(saleOrder);
        }
    }

    public Map importSaleOrderItem(SimpleRequest request){
        //Excel信息
        JSONObject importData = request.get("importData");
        com.alibaba.fastjson.JSONArray field =  request.get("data");
        JSONObject model = request.get("model");
        String fieldKey = request.get("fieldkey");
        com.alibaba.fastjson.JSONArray itemArray = model.getJSONArray("insurancePolicyFixItems");
        // 获取页面标头
        com.alibaba.fastjson.JSONArray headerHtml = new com.alibaba.fastjson.JSONArray();
        for (Object o : field) {
            headerHtml.add(((JSONObject)o).getString("name"));
        }
        // 主数据控制：凭证类型、仓库
        JSONObject scene = model.getJSONObject("scene");
        Location location = getLocation(model.getJSONObject("location").getString("id"));

        List responseListTrue = new ArrayList();
        List responseListErr = new ArrayList();
        importData.forEach((k,v)->{
            // 获取excl标头
            com.alibaba.fastjson.JSONArray excl = (com.alibaba.fastjson.JSONArray) v;
            List exclHeader = (List) excl.get(0);
            Map<String,Integer> exclHeaderMap = new HashMap();
            for (int i = 0; i < exclHeader.size(); i++) {
                exclHeaderMap.put((String)exclHeader.get(i),i);
            }
            // excl标头数据控制
            if (!(exclHeader.size() == 2 && exclHeader.get(0).equals("商品") && exclHeader.get(1).equals("数量")))
                throw new RuntimeException("导入格式请按照\"商品\"、\"数量\"的格式导入，且数据不允许为空！");

            // 排除excl首行外进行遍历
            excl.remove(0);
            for (int i = 0; i < excl.size(); i++) {
                // 将遍历行的长度与excl首行一致
                com.alibaba.fastjson.JSONArray lineData = (com.alibaba.fastjson.JSONArray) excl.get(i);
                if (lineData.size() == 0) continue; // 空行直接跳过
                while (lineData.size() < exclHeader.size()){
                    lineData.add(null);
                }
                // 遍历页面标头
                Map resultMap = new HashMap();
                for (int j = 0; j < headerHtml.size(); j++) {
                    // 匹配excl对应标头索引
                    String label = headerHtml.getString(j);
                    if (!exclHeaderMap.containsKey(label)) continue;
                    Integer index = exclHeaderMap.get(label);
                    // 获取当前行对应索引数据
                    String str = lineData.getString(index);
                    // 判断商品id是否为空
                    if (label.equals("商品") && StringUtils.isEmpty(str)) throw new RuntimeException("第"+(i+1)+"行没有商品编号");
                    resultMap.put(label,str);
                }

                // 数据修改
                String productId = (String) resultMap.get("商品");
                Product product = getProduct(productId);
                Map newMap = requestDJGC(product, location, scene);

                JSONObject priceData = new JSONObject();
                boolean noneDJGC = false;  // 是否有采购价格文件
                try {
                    priceData = getDJGC(newMap);
                } catch (Exception e) {
                    noneDJGC = true;
                }

                // 数据重构
                resultMap.put("商品",null);
                resultMap.put("productUomQty",resultMap.get("数量"));
                resultMap.put("数量",null);
                resultMap.put("product", product);
                if (noneDJGC || priceData.getDoubleValue("P004") <= 0) {
                    resultMap.put("tips","该商品无价格文件,请联系采购价格中心！");
                    responseListErr.add(resultMap);
                } else {
                    resultMap.put("priceLimit", priceData.getDoubleValue("P005"));
                    resultMap.put("suggestPrice", priceData.getDoubleValue("P004"));
                    resultMap.put("priceUnit", priceData.getDoubleValue("P004"));
                    resultMap.put("priceTotal", priceData.getDoubleValue("P004") );
                    resultMap.put("uom", product.getUom());
                    resultMap.put("marketableQty", stockRepository.findProductStock(product.getId(), location.getId()).get("STOCK_AMOUNT"));
                    responseListTrue.add(resultMap);
                }

            }
        });
        return MapUtil.mapper("responseListTrue",responseListTrue,"responseListErr",responseListErr);
    }

    private Dept getDept(Location location,JSONObject model){
        Dept dept = location.getDept();
        if (Objects.isNull(dept)){
            String deptId = model.getJSONObject("dept").getString("id");
            if (deptId == null){
                throw new RuntimeException("请传入对应的门店");
            }
            dept = deptRepository.findOne(deptId);
        }
        return dept;
    }

    private boolean isNotCommissionFlag(SaleOrder saleOrder){
        return saleOrder.getCommissionFlag() == null || !saleOrder.getCommissionFlag().equals("true");
    }

    private void unlockCouponsByDelete(SaleOrder saleOrder){
        if (isNotCommissionFlag(saleOrder)) return;
        List<SaleOrderCoupons> saleOrderCouponsList = saleOrderCouponsRepository.findAll(JpaDsl.toCriteriaByEq("saleOrderUnique", saleOrder.getUniqueValue()));
        List<Coupons> couponsList = couponsRepository.findAll(JpaDsl.toCriteriaByEq("saleOrderUnique", saleOrder.getUniqueValue()));
        //调用app接口解锁卡券
        List<String> appIds = couponsList.stream().map(coupons -> coupons.getSource()).collect(Collectors.toList());
        if (appIds != null && !appIds.isEmpty()) unlockCouponsByApp(appIds);
        //删除关联表数据
        if (saleOrderCouponsList != null && !saleOrderCouponsList.isEmpty()) saleOrderCouponsRepository.deleteAll(saleOrderCouponsList);
        //删除卡券数据
        if (couponsList != null && !couponsList.isEmpty()) couponsRepository.deleteAll(couponsList);
    }

    private void saleOrderCoupons(SaleOrder saleOrder){
        if (isNotCommissionFlag(saleOrder)) return;
        List<SaleOrderCoupons> saleOrderCouponsList = new ArrayList<>();
        saleOrder.getSaleItems().parallelStream().forEach(saleOrderItem -> {
            List<Coupons> couponsList = new ArrayList<>();
            List<Coupons> joinCoupons = JoinCoupons(saleOrderItem.getId(), (int) saleOrderItem.getProductUomQty());
            couponsList.addAll(joinCoupons);  // 根据明细与卡券的关联表查询卡券
            if (couponsList.size() >= saleOrderItem.getProductUomQty()) return;
            // 排除已经关联的数量后，通过唯一值从卡券表中获取
            couponsList.addAll(getErpCoupons(saleOrder.getUniqueValue(),couponsList,(int)saleOrderItem.getProductUomQty(),couponsList.size()));  // 根据唯一值查询卡券表(排除与关联表数据一致的)
            couponsList.stream().filter(coupons -> !joinCoupons.contains(coupons)).forEach(coupons -> {
                saleOrderCouponsList.add(getSaleOrderCoupons(coupons, saleOrderItem));
            });
        });
        saleOrderCouponsRepository.saveAll(saleOrderCouponsList);
    }

    private SaleOrderCoupons getSaleOrderCoupons(Coupons coupons,SaleOrderItem saleOrderItem){
        SaleOrderCoupons saleOrderCoupons = new SaleOrderCoupons();
        saleOrderCoupons.setSaleOrderItemId(saleOrderItem.getId());
        saleOrderCoupons.setCouponsId(coupons.getId());
        saleOrderCoupons.setCarNumber(coupons.getCarNumber());
        saleOrderCoupons.setSource(coupons.getSource());
        saleOrderCoupons.setRetailOrderId(coupons.getRetailOrderId());
        saleOrderCoupons.setPayDate(coupons.getPayDate());
        saleOrderCoupons.setName(coupons.getName());
        saleOrderCoupons.setTypeLabel(coupons.getTypeLabel());
        saleOrderCoupons.setSaleOrderUnique(coupons.getSaleOrderUnique());
        return saleOrderCoupons;
    }

    /**
     * 非编辑状态，查询明细关联的卡券
     * @param request
     * @return
     */
    public Map couponsQueryJoin(SimpleRequest request){
        JSONObject jo = request.getJO();
        String saleOrderItemId = jo.getString("saleOrderItemId");
        if (saleOrderItemId == null) return null;
        List<Coupons> couponsList = couponsRepository.findAllByJoin(saleOrderItemId);
        return MapUtil.mapper("coupons",couponsList);
    }

    /**
     * 编辑状态下，查询卡券
     * @param request
     * @return
     */
    public Map couponsQuery(SimpleRequest request){
        // 取值
        JSONObject jo = request.getJO();
        String productId = jo.getString("productId");
        int productUomQty = jo.getIntValue("productUomQty");
        String uniqueValue = jo.getString("uniqueValue");
        String saleOrderItemId = jo.getString("saleOrderItemId");
        if (uniqueValue == null){
            uniqueValue = MD5Util.encrypt(new Date().getTime() + productId + productUomQty);
        }
        // 检验入参
        Assert.isTrue(productId!=null,"商品信息异常！");
        Product product = SpringManager.getBean(ProductRepository.class).findOne(productId);
        Assert.isTrue(product!=null,"商品信息异常！");
        Assert.isTrue(productUomQty>0,"数量数据异常！");
        //获取卡券
        List<Coupons> couponsList = new ArrayList<>();
        if (saleOrderItemId != null) couponsList.addAll(JoinCoupons(saleOrderItemId,productUomQty));  // 根据明细与卡券的关联表查询卡券
        if (couponsList.size() < productUomQty) couponsList.addAll(getErpCoupons(uniqueValue,couponsList,productUomQty,couponsList.size()));  // 根据唯一值查询卡券表(排除与关联表数据一致的)
        if (couponsList.size() < productUomQty) couponsList.addAll(getAppCoupons(product,productUomQty,uniqueValue,couponsList.size()));  // 根据商品信息查询app接口
        // 封装结果
        Map result = new HashMap<>();
        result.put("uniqueValue",uniqueValue);
        result.put("coupons",couponsList);
        result.put("product",product);
        return result;
    }

    private List<Coupons> getAppCoupons(Product product,int productUomQty,String unique,int couponsSize){
        // 封装app入参
        Map param = new HashMap();
        param.put("fields", "id,name,typeLabel,usedOrderNumber,usedOrderMain.carLicense,usedOrderMain.retailNumber,usedAt");
        param.put("query", "{\"filter\":{\"and\":[]},\"perPage\":" + (productUomQty - couponsSize) + ",\"page\":1}");
        // 保险卡券商品对照表
        InsuranceCard insuranceCard = insuranceCardRepository.findAll(JpaDsl.toCriteriaByEq("product.id", product.getId())).stream().findFirst().orElse(null);
        if (insuranceCard == null){
            param.put("goodsCode", product.getId());
            param.put("categoryCode", product.getClassify().getId());
            param.put("brandCode", product.getBrand().getId());
        } else {
            param.put("categoryCode", insuranceCard.getClassify().getId());
            param.put("brandCode", insuranceCard.getBrand().getId());
        }
        // 调用app获取卡券
        JSONObject appResult = JSONObject.parseObject(this.invoke(
                "pi.app.ext", MapUtil.mapper("headers", MapUtil.mapper("method", "v1/coupon-item/get-used-insurance-coupons", "action", "get"),
                        "body", param)));
        List<Coupons> couponsList = new ArrayList<>();
        appResult.getJSONArray("data").stream().forEach(item -> {
            JSONObject itemJO = (JSONObject) item;
            Coupons coupons = new Coupons();
            coupons.setSource(itemJO.getString("id"));
            coupons.setName(itemJO.getString("name"));
            coupons.setPayDate(new Date(itemJO.getLongValue("usedAt") * 1000L));
            coupons.setTypeLabel(itemJO.getString("typeLabel"));
            if (itemJO.containsKey("usedOrderMain")) {
                JSONObject usedOrderMain = itemJO.getJSONObject("usedOrderMain");
                if (usedOrderMain.containsKey("carLicense"))
                    coupons.setCarNumber(usedOrderMain.getString("carLicense"));
                if (usedOrderMain.containsKey("retailNumber"))
                    coupons.setRetailOrderId(usedOrderMain.getString("retailNumber"));
            }
            coupons.setSaleOrderUnique(unique);
            couponsList.add(coupons);
        });
        couponsRepository.saveAll(couponsList);
        return couponsList;
    }

    private List<Coupons> getErpCoupons(String uniqueValue,List<Coupons> couponsList,int productUomQty,int couponsListSize){
        int qty = productUomQty - couponsListSize;  // 缺少数量
        List<Coupons> couponsListByUnique = couponsRepository.findAll(JpaDsl.toCriteriaByEq("saleOrderUnique", uniqueValue));
        // 过滤相同卡券
        List<String> ids = couponsList.stream().map(coupons -> coupons.getId()).collect(Collectors.toList());
        couponsListByUnique = couponsListByUnique.stream().filter(coupons -> !ids.contains(coupons.getId())).collect(Collectors.toList());
        return couponsListByUnique.size() >= qty ? couponsListByUnique.subList(0,qty) : couponsListByUnique;
    }

    public void unlockByScheduleTask(SimpleRequest request){
        List<Coupons> couponsList = couponsRepository.findAllByNotJoin();
        if (couponsList.isEmpty()) return;
        // 调用app解锁
        unlockCouponsByApp(couponsList.stream().map(coupons -> coupons.getSource()).collect(Collectors.toList()));
        // 删除数据
        couponsRepository.deleteAll(couponsList);
    }

    public Map unlock(SimpleRequest request){
        JSONObject jo = request.getJO();
        String saleOrderItemId = jo.getString("saleOrderItemId");
        JSONArray ja = jo.getJSONArray("coupons");
        List<String> ids = ja.stream().map(item -> ((JSONObject) item).getString("id")).collect(Collectors.toList());
        List<String> appIds = ja.stream().map(item -> ((JSONObject) item).getString("source")).collect(Collectors.toList());
        // 删除分销卡券关联
        if (saleOrderItemId != null) saleOrderCouponsRepository.deleteAllByCouponsId(ids,saleOrderItemId);
        // 删除并解锁（为防止将已经关联的卡券删除并解锁，需要先过滤）
        List<Coupons> couponsList = couponsRepository.findAllNotJoinAndCouponsId(ids);
        if (couponsList != null && !couponsList.isEmpty()){
            couponsRepository.deleteAllById(couponsList.stream().map(coupons -> coupons.getId()).collect(Collectors.toList()));
            unlockCouponsByApp(couponsList.stream().map(coupons -> coupons.getSource()).collect(Collectors.toList()));
        }
        return MapUtil.mapper("success","true");
    }

    private JSONObject unlockCouponsByApp(List<String> ids){
        Map param = new HashMap();
        param.put("couponItemIds",String.join(",",ids));
        JSONObject appResult = JSONObject.parseObject(this.invoke(
                "pi.app.ext", MapUtil.mapper("headers", MapUtil.mapper("method", "v1/coupon-item/unlock", "action", "post"),
                        "body", param)));
        Assert.isTrue(appResult.getBoolean("success"),"app解锁失败！");
        return appResult;
    }

    private List<Coupons> JoinCoupons(String saleOrderItemId,int productUomQty){
        List<Coupons> couponsList = couponsRepository.findAllByJoin(saleOrderItemId);
        if (couponsList.size() <= productUomQty) return couponsList;
        // 若关联卡券比当前商品数量大，将多余的关联卡券关系删除
        List<Coupons> deleteCoupons = couponsList.subList(0, couponsList.size() - productUomQty);
        List<Coupons> returnCoupons = couponsList.subList(couponsList.size() - productUomQty,couponsList.size());
        saleOrderCouponsRepository.deleteAllByCouponsId(deleteCoupons.stream().map(coupons -> coupons.getId()).collect(Collectors.toList()),saleOrderItemId);
        return returnCoupons;
    }
}
